package com.yao.ecommerce.service.async;

import com.alibaba.fastjson.JSON;
import com.yao.ecommerce.constant.GoodsConstant;
import com.yao.ecommerce.dao.EcommerceGoodsDao;
import com.yao.ecommerce.entity.EcommerceGoods;
import com.yao.ecommerce.goods.GoodsInfo;
import com.yao.ecommerce.goods.SimpleGoodsInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.IterableUtils;
import org.apache.commons.lang3.time.StopWatch;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * <h1>异步服务接口实现</h1>
 * */
@Slf4j
@Service
@Transactional
public class AsyncServiceImpl implements IAsyncService{

    @Autowired
    private EcommerceGoodsDao goodsDao;

    @Autowired
    private StringRedisTemplate redisTemplate;

    /**
     * <h2>异步任务需要加上注解, 并指定使用的线程池</h2>
     * 异步任务处理两件事:
     *  1. 将商品信息保存到数据表
     *  2. 更新商品缓存
     * */
    @Async("getAsyncExecutor")
    @Override
    public void asyncImportGoods(List<GoodsInfo> goodsInfos, String taskId) {

        log.info("async task run taskId : [{}]",taskId);
        StopWatch stopWatch = StopWatch.createStarted();

        // 1. 如果是 goodsInfo 中存在重复的商品, 不保存; 直接返回, 记录错误日志
        // 请求数据是否合法的标记
        boolean  IsIllegal = false;

        // 将商品信息字段 joint 在一起, 用来判断是否存在重复
        Set<String> goodsJointInfos = new HashSet<>(goodsInfos.size());
        // 过滤出来的, 可以入库的商品信息
        List<GoodsInfo> filterGoods = new ArrayList<>(goodsInfos.size());

        //循环遍历，过滤非法参数并判断当前请求是否合法
        for (GoodsInfo goodsInfo : goodsInfos){

            if (goodsInfo.getPrice()<=0 || goodsInfo.getSupply()<=0){
                log.info("goods info is invalid: [{}]", JSON.toJSONString(goodsInfo));
                continue;
            }

            // 组合商品信息
            String jointInfo = String.format(
                    "%s,%s,%s",
                    goodsInfo.getGoodsCategory(),
                    goodsInfo.getBrandCategory(),
                    goodsInfo.getGoodsName()
            );
            if (goodsJointInfos.contains(jointInfo)){
                IsIllegal = true;
            }
            //加入到两个容器中
            goodsJointInfos.add(jointInfo);
            filterGoods.add(goodsInfo);

        }
        // 如果存在重复商品或者是没有需要入库的商品, 直接打印日志返回
        if (IsIllegal || CollectionUtils.isEmpty(filterGoods)){
            stopWatch.stop();
            log.warn("import nothing [{}]",JSON.toJSONString(goodsInfos));
            log.info("check and import goods done: [{}ms]",
                   stopWatch.getTime(TimeUnit.MILLISECONDS));
            return;
        }
        //转换实体对象集合
        List<EcommerceGoods> ecommerceGoods = filterGoods.stream()
                .map(EcommerceGoods::to).collect(Collectors.toList());
        List<EcommerceGoods> targetGoods = new ArrayList<>();

        //保存前goodsInfo 之前判断商品是否重复
        ecommerceGoods.forEach(g->{

            if (null != goodsDao.findFirst1ByGoodsCategoryAndBrandCategoryAndGoodsName(
                    g.getGoodsCategory(),g.getBrandCategory(),g.getGoodsName()
            ).orElse(null)){
                return;
            }
            targetGoods.add(g);
        });

        //商品信息入库
        List<EcommerceGoods> savedGoods = IterableUtils.toList(goodsDao.saveAll(targetGoods));
        //将入库信息同步到redis中


        log.info("save goods info to db and redis: [{}]", savedGoods.size());

        stopWatch.stop();
        log.info("check and import goods success: [{}ms]",
                stopWatch.getTime(TimeUnit.MILLISECONDS));

    }
    /**
     * <h2>将保存到数据表中的数据缓存到 Redis 中</h2>
     * dict: key -> <id, SimpleGoodsInfo(json)>
     * */
    private void saveNewGoodsInfoToRedis(List<EcommerceGoods> saveGoods){

        // 由于 Redis 是内存存储, 只存储简单商品信息
    List<SimpleGoodsInfo> simpleGoodsInfos = saveGoods.stream()
            .map(EcommerceGoods::toSimple).collect(Collectors.toList());

    Map<String,String> id2JsonObject = new HashMap<>(simpleGoodsInfos.size());

    simpleGoodsInfos.forEach(
            g-> id2JsonObject.put(g.getId().toString(),JSON.toJSONString(g))
    );
    //保存到redis中
        redisTemplate.opsForHash().putAll(GoodsConstant.ECOMMERCE_GOODS_DICT_KEY,id2JsonObject);

    }


}
